import { IAssemblyDocTreeNode, MouseInfo } from '@amcax/base';
import {
  applyMatrix4ArrayToObject,
  getAllKeysOfRefDoc,
  PickerAssembly,
  PickerMgr,
} from '@amcax/renderer';
import { Object3D } from 'three-legacy';
import { MultiPickResult, pickedProductData } from './types';
import { allParts } from '@app-base/pages/product-support/part-funcs';
import { RefDocTypes } from '../../../pages/product-support/consts';

export class PickedProduct extends Object3D {
  constructor(
    public assembly: IAssemblyDocTreeNode,
    public skipLastMatrix = false,
  ) {
    super();
    const ids = getAllKeysOfRefDoc(assembly);
    const picker = PickerMgr.getPicker<PickerAssembly>('assembly');
    const pickedResults = ids
      .map((id) => picker.generatePickObjByPickID(id))
      .filter(Boolean);

    const isRoot = assembly.parent == null;
    pickedResults.forEach((r) => {
      if (r.assisObj) {
        this.add(r.assisObj);
        const matrixArray = allParts.get(
          r?.idInfo?.o?.split?.(':')?.[0],
        )?.matrix4Array;
        if (matrixArray) {
          applyMatrix4ArrayToObject(
            r.assisObj,
            isRoot
              ? matrixArray
              : matrixArray.slice(0, -assembly?.matrix4Array?.length),
          );
        }
        this.childObjects.push(r.assisObj);
      }
    });

    if (!isRoot && !skipLastMatrix) {
      applyMatrix4ArrayToObject(this, assembly.matrix4Array);
    }
  }

  childObjects: MultiPickResult['assisObj'][] = [];

  addTo(parent: Object3D) {
    parent?.add(this);
  }

  destroy() {
    // destroy and remove all children such as CSS2DReactObject, if any.
    // the release of the resources should be handled by their onRemoved callback
    this.children.forEach((c) => {
      (c as any).destroy?.();
    });
    this.clear();

    this.childObjects.forEach((c) => {
      c?.destroy?.();
    });

    this.parent?.remove(this);
  }

  cloneSelf() {
    return new PickedProduct(this.assembly, this.skipLastMatrix);
  }
}

export const generateProductPickObj = (
  node: IAssemblyDocTreeNode,
  mouse?: MouseInfo,
  skipLastMatrix = false,
): pickedProductData | null => {
  if (!RefDocTypes.includes(node?.type) || node == null) {
    return null;
  }

  return {
    assisObj: new PickedProduct(node, skipLastMatrix),
    assemblyInfo: node,
    idInfo: {
      o: node.key,
    },
    mouse,
    productId: node.key,
  } as pickedProductData;
};
